<template>
    <!-- 遮罩层：控制显示/遮罩交互 -->
    <div v-if="visible" class="custom-dialog-mask" :class="{
        'custom-dialog-mask--disabled': !maskClosable,
        'show-mask': showMask
    }" @click="handleMaskClick" :style="{ backgroundColor: maskColor }">
    </div>

    <!-- 弹框内容区：居中显示 + 动画 -->
    <div v-if="visible" class="custom-dialog" :style="{
        width: dialogWidth,
        maxWidth: dialogMaxWidth,
        top: dialogTop,
        zIndex: zIndex
    }" @keydown.esc="handleEscClose">
        <!-- 头部：标题 + 关闭按钮 -->
        <div class="custom-dialog__header" v-if="showHeader">
            <slot name="header">
                <h3 class="custom-dialog__title" :style="{ color: titleColor }">
                    {{ title }}
                </h3>
            </slot>
            <button class="custom-dialog__close-btn" type="button" :disabled="loading" @click="handleClose"
                aria-label="关闭弹框">
                <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                    <line x1="18" y1="6" x2="6" y2="18"></line>
                    <line x1="6" y1="6" x2="18" y2="18"></line>
                </svg>
            </button>
        </div>

        <!-- 主体内容：支持自定义插槽 + 滚动 -->
        <div class="custom-dialog__body" :style="{ maxHeight: bodyMaxHeight }">
            <!-- 加载状态 -->
            <div class="custom-dialog__loading" v-if="loading">
                <svg class="loading-spinner" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                    <circle cx="12" cy="12" r="10"></circle>
                    <path
                        d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z">
                    </path>
                </svg>
                <span class="loading-text">{{ loadingText }}</span>
            </div>

            <!-- 自定义内容插槽（默认插槽） -->
            <slot v-else></slot>
        </div>

        <!-- 底部按钮：支持自定义插槽 + 按钮配置 -->
        <div class="custom-dialog__footer" v-if="showFooter">
            <slot name="footer">
                <div class="custom-dialog__button-group">
                    <!-- 取消按钮（可选） -->
                    <button v-if="showCancelBtn" class="custom-dialog__btn custom-dialog__btn--cancel" type="button"
                        :disabled="loading" @click="handleCancel"
                        :style="{ backgroundColor: cancelBtnBg, color: cancelBtnColor }">
                        {{ cancelBtnText }}
                    </button>

                    <!-- 确认按钮（可选） -->
                    <button v-if="showConfirmBtn" class="custom-dialog__btn custom-dialog__btn--confirm" type="button"
                        :disabled="loading || confirmBtnDisabled" @click="handleConfirm"
                        :style="{ backgroundColor: confirmBtnBg, color: confirmBtnColor }">
                        <svg v-if="loading" class="confirm-loading" viewBox="0 0 24 24" fill="none"
                            stroke="currentColor" stroke-width="2">
                            <circle cx="12" cy="12" r="10"></circle>
                            <path
                                d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z">
                            </path>
                        </svg>
                        {{ confirmBtnText }}
                    </button>
                </div>
            </slot>
        </div>
    </div>
</template>

<script setup lang="ts">
import { ref, watch, onMounted, onUnmounted, PropType } from 'vue';

// ========================== 类型定义 ==========================
/** 按钮配置项类型（用于确认/取消按钮） */
interface ButtonConfig {
    show: boolean;
    text: string;
    bg: string;
    color: string;
    disabled?: boolean;
}

// ========================== 组件Props（高可配置） ==========================
const props = defineProps({
    /** 双向绑定：控制弹框显示/隐藏 */
    modelValue: {
        type: Boolean,
        required: true,
        default: false
    },

    /** 弹框标题（默认插槽可覆盖） */
    title: {
        type: String,
        default: '提示'
    },

    /** 弹框宽度（支持px/%，如"500px"、"80%"） */
    dialogWidth: {
        type: String,
        default: '500px'
    },

    /** 弹框最大宽度（防止过宽） */
    dialogMaxWidth: {
        type: String,
        default: '90vw'
    },

    /** 弹框顶部距离（支持px/%，如"100px"、"20%"） */
    dialogTop: {
        type: String,
        default: '100px'
    },

    /** 主体内容最大高度（超出滚动） */
    bodyMaxHeight: {
        type: String,
        default: '50vh'
    },

    /** 是否显示头部（标题+关闭按钮） */
    showHeader: {
        type: Boolean,
        default: true
    },

    /** 是否显示底部按钮区 */
    showFooter: {
        type: Boolean,
        default: true
    },

    /** 遮罩层是否显示 */
    showMask: {
        type: Boolean,
        default: true
    },

    /** 遮罩层点击是否关闭弹框 */
    maskClosable: {
        type: Boolean,
        default: true
    },

    /** 遮罩层颜色（支持rgba/十六进制） */
    maskColor: {
        type: String,
        default: 'rgba(0, 0, 0, 0.5)'
    },

    /** 标题颜色 */
    titleColor: {
        type: String,
        default: '#303133'
    },

    /** 取消按钮配置 */
    cancelBtn: {
        type: Object as PropType<Partial<ButtonConfig>>,
        default: () => ({
            show: true,
            text: '取消',
            bg: '#f5f7fa',
            color: '#303133'
        })
    },

    /** 确认按钮配置 */
    confirmBtn: {
        type: Object as PropType<Partial<ButtonConfig>>,
        default: () => ({
            show: true,
            text: '确认',
            bg: '#1890ff',
            color: '#ffffff',
            disabled: false
        })
    },

    /** 是否显示加载状态（覆盖内容区） */
    loading: {
        type: Boolean,
        default: false
    },

    /** 加载状态文本 */
    loadingText: {
        type: String,
        default: '处理中...'
    },

    /** 弹框层级（z-index） */
    zIndex: {
        type: Number,
        default: 1000
    },

    /** 弹框显示时是否禁止页面滚动 */
    preventScroll: {
        type: Boolean,
        default: true
    }
});

// ========================== 解构Props（处理默认值） ==========================
const {
    cancelBtn,
    confirmBtn,
    showMask,
    preventScroll,
    modelValue,
    maskClosable
} = props;

// 取消按钮最终配置（合并默认值）
const showCancelBtn = cancelBtn.show ?? true;
const cancelBtnText = cancelBtn.text ?? '取消';
const cancelBtnBg = cancelBtn.bg ?? '#f5f7fa';
const cancelBtnColor = cancelBtn.color ?? '#303133';

// 确认按钮最终配置（合并默认值）
const showConfirmBtn = confirmBtn.show ?? true;
const confirmBtnText = confirmBtn.text ?? '确认';
const confirmBtnBg = confirmBtn.bg ?? '#1890ff';
const confirmBtnColor = confirmBtn.color ?? '#ffffff';
const confirmBtnDisabled = confirmBtn.disabled ?? false;

// 弹框显示状态（同步modelValue）
const visible = ref(modelValue);

// ========================== 组件Emits（事件反馈） ==========================
const emit = defineEmits<{
    /** 双向绑定：更新显示状态 */
    (e: 'update:modelValue', value: boolean): void;
    /** 确认按钮点击事件 */
    (e: 'confirm'): void;
    /** 取消按钮点击事件 */
    (e: 'cancel'): void;
    /** 弹框关闭事件（包括遮罩、关闭按钮、ESC键） */
    (e: 'close'): void;
}>();

// ========================== 核心逻辑 ==========================
/** 控制页面滚动（显示时禁止，隐藏时恢复） */
const controlPageScroll = (isShow: boolean) => {
    if (!preventScroll) return;
    const body = document.body;
    if (isShow) {
        body.style.overflow = 'hidden';
        body.style.paddingRight = `${getScrollbarWidth()}px`; // 避免页面抖动
    } else {
        body.style.overflow = '';
        body.style.paddingRight = '';
    }
};

/** 获取浏览器滚动条宽度（用于补偿页面抖动） */
const getScrollbarWidth = (): number => {
    const outer = document.createElement('div');
    outer.style.width = '100px';
    outer.style.height = '100px';
    outer.style.overflow = 'scroll';
    document.body.appendChild(outer);

    const inner = document.createElement('div');
    inner.style.width = '100%';
    outer.appendChild(inner);

    const width = outer.offsetWidth - inner.offsetWidth;
    document.body.removeChild(outer);
    return width;
};

/** 关闭弹框（统一处理） */
const handleClose = () => {
    visible.value = false;
    emit('update:modelValue', false);
    emit('close');
};

/** 确认按钮点击 */
const handleConfirm = () => {
    emit('confirm');
    // 若需要自动关闭，可添加：handleClose();
};

/** 取消按钮点击 */
const handleCancel = () => {
    emit('cancel');
    handleClose();
};

/** 遮罩层点击 */
const handleMaskClick = () => {
    if (maskClosable && showMask) {
        handleClose();
    }
};

/** ESC键关闭弹框 */
const handleEscClose = () => {
    handleClose();
};

// ========================== 监听与生命周期 ==========================
// 监听modelValue变化（同步显示状态）
watch(
    () => props.modelValue,
    (newVal) => {
        visible.value = newVal;
        controlPageScroll(newVal); // 同步控制滚动
    },
    { immediate: true }
);

// 挂载时初始化滚动状态
onMounted(() => {
    controlPageScroll(visible.value);
});

// 卸载时恢复页面滚动
onUnmounted(() => {
    controlPageScroll(false);
});

// ========================== 暴露API（可选） ==========================
defineExpose({
    handleClose, // 允许父组件直接调用关闭方法
    visible      // 暴露当前显示状态
});
</script>

<style scoped lang="scss">
// 遮罩层样式
.custom-dialog-mask {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    transition: background-color 0.3s ease;
    cursor: pointer;
    z-index: v-bind(zIndex);

    &--disabled {
        cursor: default;
    }

    // 隐藏遮罩（通过showMask控制）
    &:not(.show-mask) {
        display: none;
    }
}

// 弹框主体样式
.custom-dialog {
    position: fixed;
    left: 50%;
    transform: translateX(-50%);
    background-color: #ffffff;
    border-radius: 8px;
    box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
    transition: opacity 0.3s ease, transform 0.3s ease;
    z-index: v-bind(zIndex + 1); // 确保弹框在遮罩之上

    // 头部样式
    &__header {
        display: flex;
        align-items: center;
        justify-content: space-between;
        padding: 16px 24px;
        border-bottom: 1px solid #f0f0f0;

        &__title {
            margin: 0;
            font-size: 16px;
            font-weight: 500;
        }

        &__close-btn {
            display: flex;
            align-items: center;
            justify-content: center;
            width: 24px;
            height: 24px;
            border: none;
            background: transparent;
            color: #909399;
            cursor: pointer;
            transition: color 0.2s ease;

            &:hover {
                color: #303133;
            }

            &:disabled {
                cursor: not-allowed;
                opacity: 0.6;
            }
        }
    }

    // 主体内容样式
    &__body {
        padding: 24px;
        overflow-y: auto;
        font-size: 14px;
        color: #606266;
        line-height: 1.5;

        // 加载状态样式
        &__loading {
            display: flex;
            align-items: center;
            justify-content: center;
            padding: 30px 0;
            flex-direction: column;
            gap: 12px;

            .loading-spinner {
                width: 24px;
                height: 24px;
                color: v-bind(confirmBtnBg);
                animation: spin 1.5s linear infinite;
            }

            .loading-text {
                font-size: 14px;
                color: #606266;
            }
        }
    }

    // 底部按钮样式
    &__footer {
        display: flex;
        align-items: center;
        justify-content: flex-end;
        padding: 16px 24px;
        border-top: 1px solid #f0f0f0;
        gap: 12px;

        &__button-group {
            display: flex;
            gap: 12px;
        }

        &__btn {
            padding: 8px 16px;
            border: none;
            border-radius: 4px;
            font-size: 14px;
            font-weight: 500;
            cursor: pointer;
            transition: opacity 0.2s ease, background-color 0.2s ease;

            &:disabled {
                cursor: not-allowed;
                opacity: 0.6;
            }

            // 确认按钮额外样式
            &--confirm {
                &:hover {
                    opacity: 0.9;
                }
            }

            // 取消按钮额外样式
            &--cancel {
                &:hover {
                    background-color: #e8ebf0;
                }
            }

            // 确认按钮加载图标
            .confirm-loading {
                width: 16px;
                height: 16px;
                margin-right: 6px;
                animation: spin 1.5s linear infinite;
            }
        }
    }
}

// 滚动动画
@keyframes spin {
    from {
        transform: rotate(0deg);
    }

    to {
        transform: rotate(360deg);
    }
}
</style>